﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

using AForge.Video.FFMPEG;
using AForge.Video;
using System.Diagnostics;
using System.Reflection;

namespace ScreenRecord
{
    public partial class Form1 : Form
    {
        private bool _isRecording;
        private List<string> _screenNames;
        //private Rectangle _screenSize;
        private UInt32 _frameCount;
		private int fps = 0;
		private VideoFileWriter _writer;
        private int _width;
        private int _height;
        private ScreenCaptureStream _streamVideo;
        private Stopwatch _stopWatch;
        private Rectangle _screenArea;
        int screenLeft, screenTop = 0;
		bool useArea = false;

		[StructLayout(LayoutKind.Sequential)]
        struct CURSORINFO
        {
            public Int32 cbSize;
            public Int32 flags;
            public IntPtr hCursor;
            public POINTAPI ptScreenPos;
        }

        [StructLayout(LayoutKind.Sequential)]
        struct POINTAPI
        {
            public int x;
            public int y;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct ICONINFO
        {
            public bool fIcon;
            public Int32 xHotspot;
            public Int32 yHotspot;
            public IntPtr hbmMask;
            public IntPtr hbmColor;
        }

        [DllImport("user32.dll")]
        static extern bool GetCursorInfo(out CURSORINFO pci);

        [DllImport("user32.dll")]
        public static extern IntPtr CopyIcon(IntPtr hIcon);

        [DllImport("user32.dll")]
        public static extern bool GetIconInfo(IntPtr hIcon, out ICONINFO piconinfo);

        [DllImport("user32.dll")]
        static extern bool DrawIcon(IntPtr hDC, int X, int Y, IntPtr hIcon);

        const Int32 CURSOR_SHOWING = 0x00000001;

        public Form1()
        {
            InitializeComponent();

            this._isRecording = false;
            //this._screenSize = Screen.PrimaryScreen.Bounds;
            this._frameCount = 0;
            this._width = SystemInformation.VirtualScreen.Width;
            this._height = SystemInformation.VirtualScreen.Height;
            this._stopWatch = new Stopwatch();
            this._screenArea = Rectangle.Empty;
			
            this.bt_Save.Enabled = false;
            this._writer = new VideoFileWriter();

            _screenNames = new List<string>();
            _screenNames.Add( @"Select ALL" );
			_screenNames.Add(@"Custom screen area");
			foreach ( var screen in Screen.AllScreens )
            {
                _screenNames.Add(screen.DeviceName);
            }
            this.cb_screenSelector.DropDownStyle = ComboBoxStyle.DropDownList;
            this.cb_screenSelector.DataSource = _screenNames;

            // Codec ComboBox
            this.cb_VideoCodec.DataSource = Enum.GetValues( typeof( VideoCodec ) );
            this.cb_VideoCodec.DropDownStyle = ComboBoxStyle.DropDownList;

            // BitRate 2000kbit/s 2000000 1000000
            this.cb_BitRate.DataSource = Enum.GetValues( typeof( BitRate ) );
            this.cb_BitRate.DropDownStyle = ComboBoxStyle.DropDownList;
            this.cb_BitRate.SelectedIndex = 3;

			tb_SaveFolder.Text = System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
		}

		private void bt_start_Click(object sender, EventArgs e)
		{
			Start(false);
		}

		private void Start(bool selectArea)
		{ 
            try
            {
                if ( this.tb_SaveFolder.Text.Length < 1 )
                {
                    FolderBrowserDialog fbd = new FolderBrowserDialog();
                    if ( fbd.ShowDialog() != DialogResult.OK )
                    {
						MessageBox.Show("You should choose a path to save the video file!");
						return;
					}
                }

				this.StartRec( this.tb_SaveFolder.Text, selectArea );
            }
            catch ( Exception exc )
            {
                MessageBox.Show( exc.Message );
            }
        }

        private void StartRec( string path, bool selectArea )
        {
            if ( _isRecording == false )
            {
                this.SetScreenArea(selectArea);

                this.SetVisible( true );

                this._frameCount = 0;

                this.tb_SaveFolder.Text = path;
                string fullName = string.Format( @"{0}\{1}_{2}.avi", path, Environment.UserName.ToUpper(), DateTime.Now.ToString( "d_MMM_yyyy_HH_mm_ssff" ) );

                // Save File option
                _writer.Open(
                    fullName,
                    this._width,
                    this._height,
                    ( int ) nud_FPS.Value,
                    ( VideoCodec ) cb_VideoCodec.SelectedValue,
                    ( int ) ( BitRate ) this.cb_BitRate.SelectedValue );

                // Start main work
                this.StartRecord();
            }
        }

        private void SetScreenArea(bool selectArea)
        {
			screenLeft = screenTop = 0;
			useArea = false;

			// get entire desktop area size
			string screenName = this.cb_screenSelector.SelectedValue.ToString();
			if (string.Compare(screenName, @"Select ALL", StringComparison.OrdinalIgnoreCase) == 0)
			{
				foreach (Screen screen in Screen.AllScreens)
				{
					this._screenArea = Rectangle.Union(_screenArea, screen.Bounds);
					this._width = _screenArea.Width;
					this._height = _screenArea.Height;
				}
			}
			else if (string.Compare(screenName, @"Custom screen area", StringComparison.OrdinalIgnoreCase) == 0)
			{
				using (TopForm f = new TopForm())
				{

					if (f.ShowDialog() == DialogResult.OK)
					{
						this.WindowState = FormWindowState.Minimized;
						this._screenArea = f.AreaBounds;

						decimal prop = (decimal)4 / 3;
						decimal realProp = (decimal) f.w / f.h;
						bool makeLonger = realProp < prop;
						int w = Convert.ToInt32(makeLonger ? f.h * prop : f.w);
						int h = Convert.ToInt32(makeLonger ? f.h : f.w / prop);

						if ((w & 1) != 0)
							w = w + 1;
						if ((h & 1) != 0)
							h = h + 1;

						this._width = w;
						this._height = h;
						screenLeft = f.AreaBounds.Left;
						screenTop = f.AreaBounds.Top;
						useArea = true;
                    }

				}
				
			}
			else
            {
                this._screenArea = Screen.AllScreens.First( scr => scr.DeviceName.Equals( screenName ) ).Bounds;
                this._width = this._screenArea.Width;
                this._height = this._screenArea.Height;
            }
        }

        private void StartRecord() //Object stateInfo
        {
            // create screen capture video source
            this._streamVideo = new ScreenCaptureStream( this._screenArea );

            // set NewFrame event handler
            this._streamVideo.NewFrame += new NewFrameEventHandler( this.video_NewFrame );

            // start the video source
            this._streamVideo.Start();

            // _stopWatch
            this._stopWatch.Start();
        }

        private void video_NewFrame( object sender, NewFrameEventArgs eventArgs )
        {
			if (this._isRecording)
			{
				this._frameCount++;

				Bitmap frame = eventArgs.Frame;


				Graphics graphics = Graphics.FromImage(frame);


				CURSORINFO pci;
				pci.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(typeof(CURSORINFO));

				if (GetCursorInfo(out pci))
				{
					if (pci.flags == CURSOR_SHOWING)
					{
						int x = pci.ptScreenPos.x - screenLeft;
						int y = pci.ptScreenPos.y - screenTop;

						Color c = Color.Yellow;
						float width = 2;
						int radius = 30;
						if ((Control.MouseButtons & MouseButtons.Left) != 0 || (Control.MouseButtons & MouseButtons.Right) != 0)
						{
							c = Color.OrangeRed;
							width = 4;
							radius = 35;
						}
						Pen p = new Pen(c, width);

						graphics.DrawEllipse(p, x - radius / 2, y - radius / 2, radius, radius);
						DrawIcon(graphics.GetHdc(), x, y, pci.hCursor);
						graphics.ReleaseHdc();
					}
				}
				

				if (useArea)
				{ 
					var destRect = new Rectangle(Convert.ToInt32((_width - frame.Width) / 2), Convert.ToInt32((_height - frame.Height) / 2), frame.Width, frame.Height);
					var destImage = new Bitmap(_width, _height);
					destImage.SetResolution(frame.HorizontalResolution, frame.VerticalResolution);

					graphics = Graphics.FromImage(destImage);
					graphics.DrawImage(frame, destRect, 0, 0, frame.Width, frame.Height, GraphicsUnit.Pixel, null);
					frame = destImage;
                }

				this._writer.WriteVideoFrame(frame);
				

                this.lb_1.Invoke( new Action( () =>
                {
                    lb_1.Text = string.Format( @"Frames: {0}", _frameCount );
                } ) );

				this.label6.Invoke(new Action(() =>
				{
					label6.Text = string.Format(@"Avg fps: {0}", Convert.ToInt32(_frameCount/ _stopWatch.Elapsed.Seconds));
				}));

				this.lb_stopWatch.Invoke( new Action( () =>
                {
                    this.lb_stopWatch.Text = _stopWatch.Elapsed.ToString();
                } ) );
            }
            else
            {
                _stopWatch.Reset();
                Thread.Sleep( 500 );
                _streamVideo.SignalToStop();
                Thread.Sleep( 500 );
                _writer.Close();
            }
        }

        private void bt_Save_Click( object sender, EventArgs e )
        {
            this.SetVisible( false );
            MessageBox.Show( @"File saved!" );
        }

        private void SetVisible( bool visible )
        {
            this.bt_start.Enabled = !visible;
            this.bt_Save.Enabled = visible;
            this._isRecording = visible;
        }

		private void browseBtn_Click(object sender, EventArgs e)
		{
			FolderBrowserDialog fbd = new FolderBrowserDialog();
			fbd.ShowNewFolderButton = true;
			if (!System.IO.Directory.Exists(tb_SaveFolder.Text))
				fbd.RootFolder = Environment.SpecialFolder.MyComputer;
			else 
				fbd.SelectedPath = tb_SaveFolder.Text;

            if (fbd.ShowDialog() == DialogResult.OK)
			{
				tb_SaveFolder.Text = fbd.SelectedPath;
            } 
        }

		private void Form1_FormClosing( object sender, FormClosingEventArgs e )
        {
            this._isRecording = false;
        }

    }

    public enum BitRate
    {
        _50kbit = 5000,
        _100kbit = 10000,
        _500kbit = 50000,
        _1000kbit = 1000000,
        _2000kbit = 2000000,
        _3000kbit = 3000000,
        _4000kbit = 4000000,
        _5000kbit = 5000000
    }
}
